package vampirism.core.client;

import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input.Keys;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Animation;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Dialog;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.utils.Scaling;
import vampirism.core.shared.*;
import vampirism.glutils.ShapeRenderer;
import vampirism.glutils.ShapeRenderer.ShapeType;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;


/**
 * Main class - does the following things:
 * 1) Initializes all game variables
 * 2) Renders everything
 * 3) Connects to all other classes (you can access everything from here)
 * @author PC
 *
 */
public class MainGame implements ApplicationListener {

	float W, H;

	Skin skin;
	Stage stage;
	OrthographicCamera camera;
	OrthographicCamera cameraText;
	SpriteBatch batch;
	SpriteBatch batchText;
	ShapeRenderer emptyRenderer;
	ShapeRenderer filledRenderer;
	
	MenuLogin menuLogin;
	MenuCreate menuCreate;
	MenuSelect menuSelect;
	MenuResult menuResult;
	
	public ClientSide clientSide;
	public Updater clientUpdater;
	public ClientInput clientInput;
	public ClientTextureParser clientTextureParser;
	
	public Message depthMsg;
	
	public Random rand = new Random(Network.seed);

	public String[] playerTypeNames = { "Warrior",  "Priest", "Archer", "Rogue", "Mage"  };
	public Texture spriteSheetPlayer;
	public Texture spriteSheetNpcBig;
	public Texture spriteSheetNpcSmall;
	public Texture spriteSheetBullets;
	public Texture spriteSheetBiomes;
	
	public TextureRegion currentFrame;
	
	public TextureRegion[] framesPlayer;//contains all individual player frames
	public TextureRegion[] framesPlayerFlipped;//contains all individual player frames flipped
	
	public TextureRegion[] framesNpc;//contains all individual player frames
	public TextureRegion[] framesNpcFlipped;//contains all individual player frames flipped
	
	public TextureRegion[] framesBullet;
 	
	public Animation aniRightPlayer[] = new Animation[6]; //contains a list of all RIGHT animations
	public Animation aniLeftPlayer[] = new Animation[6]; //contains a list of all LEFT animations
	public Animation aniDownPlayer[] = new Animation[6]; //contains a list of all DOWN animations
	public Animation aniUpPlayer[] = new Animation[6]; //contains a list of all UP animations
	
	public Animation aniRightNpc[] = new Animation[16]; //contains a list of all RIGHT animations
	public Animation aniLeftNpc[] = new Animation[16]; //contains a list of all LEFT animations
	
	public Color playerColor1 = new Color(0.1f,0.41f,0.95f,1.0f);
	public Color playerColor2 = new Color(0.8f,0.41f,0.25f,1.0f);
	public Color bulletColor = new Color(0.7f,0.7f,0.7f,1.0f);
	public Color tileColor1 = new Color(0.5f,0.41f,0.95f,1.0f);
	public Color tileColor2 = new Color(0.95f,0.21f,0.25f,1.0f);
	public Color healthColor = new Color(0.8f,0.1f,0.1f,1.0f);
	public Color energyColor = new Color(0.1f,0.1f,0.8f,1.0f);
	public Color normalColor = new Color(1.0f,1.0f,1.0f,1.0f);
	public Color vampireColor = new Color(0.9f,0.2f,0.3f,1.0f);
	
	
	//Map of the game - consists of Tiles
	public Tile[][] map;
	
	//List of all biomes (each biome contains Tiles with the same biomeNum, and same texture)
	public List<Biome> biomes;
	
	//A list of all bullet in the current game
	public List<Bullet> bullets = new ArrayList<Bullet>();

	// A list of characters that this account has - initially is empty
	// This list is only filled after successfully registering or successfully
	// logging in
	public List<Player> myPlayers = new ArrayList<Player>();

	// A list of all characters that currently are online in the game
	public List<Player> players = new ArrayList<Player>();

	// The player that you control
	// Is set only after receiving SelectPlayerResult packet
	public Player myPlayer = null;
	
	//Just temporary variables - to save memory
	public Player tempPlayer = null;
	public Tile tempTile = null;
	public Rectangle tempRect = new Rectangle(1,1,1,1);
	public int tempI;
	public Vector2 tempIndex;

	private Bullet tempBullet;

	

	

	@Override
	public void create() {

		W = Gdx.graphics.getWidth();
		H = Gdx.graphics.getHeight();
		
		map = Tile.createMap();
		biomes = Creator.createBiomes(map);

		batch = new SpriteBatch();
		batchText = new SpriteBatch();
		Texture.setEnforcePotImages(false);
		skin = new Skin(Gdx.files.internal("skin/uiskin.json"));
		stage = new Stage(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(),false);
		camera = new OrthographicCamera(1, W / H);
		cameraText = new OrthographicCamera(1, W / H);
		emptyRenderer = new ShapeRenderer();
		filledRenderer = new ShapeRenderer();
		
		spriteSheetPlayer = new Texture(Gdx.files.internal("data/skinsPlayer.png"));
		spriteSheetNpcBig = new Texture(Gdx.files.internal("data/skinsMonstersBig.png"));
		spriteSheetNpcSmall = new Texture(Gdx.files.internal("data/skinsMonstersSmall.png"));
		spriteSheetBullets = new Texture(Gdx.files.internal("data/projectiles.png"));
		spriteSheetBiomes = new Texture(Gdx.files.internal("data/biomes.png"));
		
		depthMsg = new Message(new Rectangle(15, 580, 300, 30), "", 99999999);

		clientSide = new ClientSide(this);
		clientInput = new ClientInput(this);
		clientUpdater = new Updater(map,players,bullets,biomes);
		clientTextureParser = new ClientTextureParser(this);
		
		clientUpdater.mainGame = this;
		
		//clientTextureParser.getNpcTextures();
		clientTextureParser.getBulletTextures();
		clientTextureParser.getPlayerTextures();
		clientTextureParser.getBiomeTextures();
		
		
		Thread clientConnectionThread = new Thread(clientSide);
		clientConnectionThread.start();
		
		Thread updateThread = new Thread(clientUpdater);
		updateThread.start();
		
		camera.setToOrtho(false, W, H);
		camera.zoom = 0.25f;
		cameraText.setToOrtho(false, W, H);

		changeMenu(0);// Initialize login menu

		Gdx.input.setInputProcessor(stage);
	}



	public void changeMenu(int num) {
		stage.clear();

		switch (num) {
		case 0:// Login/Register
			menuLogin = new MenuLogin(this);
			stage.addActor(menuLogin.window);
			break;
		case 1:// Create new character
			menuCreate = new MenuCreate(this);
			stage.addActor(menuCreate.window);
			break;
		case 2:// Select character
			menuSelect = new MenuSelect(this);
			stage.addActor(menuSelect.playerWindow);
			stage.addActor(menuSelect.infoWindow);
			break;
		case 3:// Play the game

			break;
		case 4:// GameResult world is saved!
			menuResult = new MenuResult(this,true);
			stage.addActor(menuResult.window);
			break;
		case 5:// GameResult world is doomed!
			menuResult = new MenuResult(this,false);
			stage.addActor(menuResult.window);
			break;
		}
	}

	public void showInfo(String message) {
		Dialog dialog = new Dialog("info", skin, "dialog") {
			protected void result(Object object) {
				if (menuLogin != null)
					menuLogin.confirmButton.setDisabled(false);
				if (menuCreate != null)
					menuCreate.createButton.setDisabled(false);
				if (menuSelect != null)
					menuSelect.playButton.setDisabled(false);
			}
		};
		dialog.text(message).button("OK", true).key(Keys.ENTER, true)
				.key(Keys.ESCAPE, false).show(stage);
	}
	

	@Override
	public void render() {
		
		batch.setProjectionMatrix(camera.combined);
		batchText.setProjectionMatrix(cameraText.combined);
		emptyRenderer.setProjectionMatrix(camera.combined);
		filledRenderer.setProjectionMatrix(camera.combined);	
		
		Gdx.gl.glClearColor(0.2f, 0.2f, 0.2f, 1);
		Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
		stage.act(Math.min(Gdx.graphics.getDeltaTime(), 1 / 30f));
		if(stage!=null)stage.draw();
		
		
		//Can only get past this point if we have created a player!
		if(myPlayer==null)return;
		
		clientInput.update();

		camera.position.set(myPlayer.pos.x, myPlayer.pos.y, 0);
		cameraText.position.set(myPlayer.pos.x, myPlayer.pos.y, 0);
		
		camera.update();
		cameraText.update();
		
		batch.begin();
		batch.setColor(normalColor);
		//Render TEXTURE of all Tiles (map)
		for(int i = -8; i < 9; i++){
			for(int j = -8; j < 9; j++){
				tempTile = clientUpdater.getLocationTile(myPlayer.pos.x+8*i, myPlayer.pos.y+8*j);
				if(tempTile!=null && !tempTile.solid)batch.draw(tempTile.texture,tempTile.pos.x, tempTile.pos.y);
			}
		}
		batch.end();
		
		emptyRenderer.begin(ShapeType.Rectangle);
		filledRenderer.begin(ShapeType.FilledRectangle);	
		
			//Render POSITION of all Tiles (map)
			for(int i = -8; i < 9; i++){
				for(int j = -8; j < 9; j++){
					tempTile = clientUpdater.getLocationTile(myPlayer.pos.x+8*i, myPlayer.pos.y+8*j);
					if(tempTile!=null){
						if(!tempTile.solid){
							emptyRenderer.setColor(tileColor1);
							emptyRenderer.rect(tempTile.pos.x, tempTile.pos.y, tempTile.pos.width, tempTile.pos.height);
						}
					}
				}
			}
			//Render POSITION solid tiles (different color, above normal tiles)
			for(int i = -8; i < 9; i++){
				for(int j = -8; j < 9; j++){
					tempTile = clientUpdater.getLocationTile(myPlayer.pos.x+8*i, myPlayer.pos.y+8*j);
					if(tempTile!=null){
						if(tempTile.solid){
							emptyRenderer.setColor(tileColor2);
							emptyRenderer.rect(tempTile.pos.x, tempTile.pos.y, tempTile.pos.width, tempTile.pos.height);
						}
					}
				}
			}
		
			//Render POSITION of my player
			emptyRenderer.setColor(playerColor1);
			emptyRenderer.rect(myPlayer.pos.x, myPlayer.pos.y, myPlayer.pos.width, myPlayer.pos.height);
			
			//Render POSITION other players
			for(int i = 0; i < players.size(); i++){
				tempPlayer = players.get(i);
				emptyRenderer.setColor(playerColor2);
				emptyRenderer.rect(tempPlayer.pos.x, tempPlayer.pos.y, tempPlayer.pos.width, tempPlayer.pos.height);
				renderHealthBar(tempPlayer);
				//renderEnergyBar(tempPlayer);
			}
			
		filledRenderer.end();
		emptyRenderer.end();
		
		
		batch.begin();
		
			for(int i = 0; i < players.size(); i++){
				tempPlayer = players.get(i);
				//Render TEXTURE of other players
				if(tempPlayer.moving[0] || tempPlayer.moving[1] || tempPlayer.moving[2] || tempPlayer.moving[3]){
				
					tempPlayer.animTime += Gdx.graphics.getDeltaTime();
					if(tempPlayer.lastDir == 0)currentFrame = aniRightPlayer[tempPlayer.type].getKeyFrame(tempPlayer.animTime, true);
					if(tempPlayer.lastDir == 1)currentFrame = aniLeftPlayer[tempPlayer.type].getKeyFrame(tempPlayer.animTime, true);
					if(tempPlayer.lastDir == 2)currentFrame = aniDownPlayer[tempPlayer.type].getKeyFrame(tempPlayer.animTime, true);
					if(tempPlayer.lastDir == 3)currentFrame = aniUpPlayer[tempPlayer.type].getKeyFrame(tempPlayer.animTime, true);
				}else{
					if(tempPlayer.lastDir == 0)currentFrame = framesPlayer[0+21*tempPlayer.type];
					if(tempPlayer.lastDir == 1)currentFrame = framesPlayerFlipped[0+21*tempPlayer.type];
					if(tempPlayer.lastDir == 2)currentFrame = framesPlayer[7+21*tempPlayer.type];
					if(tempPlayer.lastDir == 3)currentFrame = framesPlayer[14+21*tempPlayer.type];
				}
                
				if(tempPlayer.vampire)
					batch.setColor(vampireColor);
				else
					batch.setColor(normalColor);
					
				if(!tempPlayer.dead){
						
					 if (tempPlayer.alpha) {
					     batch.setColor(0f, 0f, 0f, 1f);
					     //batch.draw(currentFrame,tempPlayer.pos.x-0.5f-2, tempPlayer.pos.y-0.5f-2,12,12);
					     //batch.draw(currentFrame,tempPlayer.pos.x+0.5f-2, tempPlayer.pos.y+0.5f-2,12,12);
					     batch.setColor(vampireColor);  
					     batch.draw(currentFrame,tempPlayer.pos.x-2, tempPlayer.pos.y-2,12,12);//Alpha is a little bigger visually
					 }else{
						 batch.draw(currentFrame,tempPlayer.pos.x, tempPlayer.pos.y);
					 }
				}
			}
			
		batch.end();
		
		batch.begin();
		emptyRenderer.begin(ShapeType.Rectangle);
		//Render POSITION and TEXTURE of all bullets
			for(int i = bullets.size()-1; i >= 0; i--){
				tempBullet = bullets.get(i);
				try{
                    emptyRenderer.rect(tempBullet.pos.x, tempBullet.pos.y, tempBullet.pos.width, tempBullet.pos.height);
                } catch (Exception e){
                    e.printStackTrace();
                }

				if(players.size() > tempBullet.sourceId){
					//Determine bullet color
					if(players.get(tempBullet.sourceId).vampire)
						batch.setColor(vampireColor);
					else
						batch.setColor(bulletColor);
					
					//Don't show dead people bullets
					if(players.get(tempBullet.sourceId).dead)continue;
					
					batch.draw (framesBullet[tempBullet.type*16+tempBullet.tier], tempBullet.pos.x, tempBullet.pos.y,4,4, 8,8, 1,1, -45f - (float)Math.toDegrees(tempBullet.rotation));
					
				}

			}
		batch.end();
		emptyRenderer.end();
		
		
		batchText.begin();
			//Show all player names, main camera has 4x zoom, but text camera has 1x zoom, so need to multiply diff by 4
			for(int i = 0; i < players.size(); i++){
				tempPlayer = players.get(i);
				float diffX = tempPlayer.pos.x - myPlayer.pos.x;
				float diffY = tempPlayer.pos.y - myPlayer.pos.y;
				depthMsg.font.draw(batchText, tempPlayer.name, myPlayer.pos.x+2+(diffX*4), myPlayer.pos.y+51+(diffY*4));
			}	
			depthMsg.font.draw(batchText, "Position: "+(int)myPlayer.pos.x+", "+(int)myPlayer.pos.y, myPlayer.pos.x-(240 * (H / 500)), myPlayer.pos.y+(240 * (H / 500)));
			depthMsg.font.draw(batchText, "Level: "+(int)myPlayer.level, myPlayer.pos.x-(240 * (H / 500)), myPlayer.pos.y+(220 * (H / 500)));
			depthMsg.font.draw(batchText, "Exp: "+(int)myPlayer.exp, myPlayer.pos.x-(240 * (H / 500)), myPlayer.pos.y+(200 * (H / 500)));
			
		batchText.end();
	}
	
	public void renderHealthBar(Body body){
		//Render health bar
		filledRenderer.setColor(healthColor);
		float percentHealth = body.health/(1.0f*body.statsTotal[6]);
		filledRenderer.filledRect(body.pos.x, body.pos.y-2.2f, 8*percentHealth, 0.7f);
	}
	
	public void renderEnergyBar(Body body){
		//Render energy bar
		filledRenderer.setColor(energyColor);
		float percentEnergy = body.energy/(1.0f*body.statsTotal[7]);
		filledRenderer.filledRect(body.pos.x, body.pos.y-3.2f, 8*percentEnergy, 0.7f);
	}

	@Override
    public void resize (int width, int height) {
            Vector2 size = Scaling.fit.apply(500, 500, width, height);
            int viewportX = (int)(width - size.x) / 2;
            int viewportY = (int)(height - size.y) / 2;
            int viewportWidth = (int)size.x;
            int viewportHeight = (int)size.y;
            Gdx.gl.glViewport(viewportX, viewportY, viewportWidth, viewportHeight);
            stage.setViewport(500, 500, true, viewportX, viewportY, viewportWidth, viewportHeight);
            //cameraText = new OrthographicCamera(viewportWidth / 2, viewportHeight / 2);
            cameraText.setToOrtho(false, viewportWidth, viewportHeight);
            W = width;
            H = height;
    }

	@Override
	public void dispose() {
		stage.dispose();
        //myPlayer.con.close();
	}

	@Override
	public void pause() {
	}

	@Override
	public void resume() {
	}

}